﻿#include <iostream>
#include <windows.h>
#include <thread>
#pragma comment(lib,"ws2_32.lib")

#define PARAM_SIZE 2048
#define MAX_LISTEN 5
#define MAX_DATABLOCK 1024

#define LOG(...)  do { fprintf(stderr,"%s %s ",__DATE__,__TIME__); fprintf(stderr, __VA_ARGS__); } while(0)

using namespace std;

int WORK_MODE = 0;	// 工作模式 0 为普通代理 1为反向代理

char local_host[128] = "127.0.0.1";			// 本地主机
int local_port = 7202;						// 本地监听端口
char remote_host[128] = "39.106.164.33";	// 流量转发服务器
int remote_port = 7201;						// 流量转发服务器端口


// 解析命令行参数 返回参数名，
bool getopt(int argc, char* argv[],const char key[5],char *value)
{
	for (int i = 1; i < argc; i++)
	{
		if (!strcmp(key, argv[i]))
		{
			if (i + 1 < argc)
			{
				if (argv[i + 1][0] == '-')
				{
					cout<<key << " 需要附带值" << endl;
				}
				memcpy(value, argv[i + 1], strlen(argv[i + 1]));
				return true;
			}
			else
			{
				cout << "参数长度错误" << endl;
				return false;
			}
		}
	}
	memset(value, 0, sizeof(value));	// 清空参数
	return true;
}

void ShowHelp()
{
	cout << "=====================\n"
		<<"帮助:\n"
		<<"\t反向代理\n"
		<< "\t-h：指定流量转发服务器，同时设置工作模式为反向代理\n"
		<< "\t-p: 反向代理时，由此参数指定转发服务器端口\n\n"
		<<"\t正向代理\n"
		<< "\t-l: 正向代理时，指定正向代理本地\n"
		<< endl;
}

// 创建一个服务
SOCKET StartServer()
{
	SOCKADDR_IN addr_server;
	addr_server.sin_family = AF_INET;
	addr_server.sin_addr.S_un.S_addr = inet_addr(local_host);
	addr_server.sin_port = htons(local_port);

	SOCKET server_socket = socket(AF_INET, SOCK_STREAM, 0);
	if (server_socket == SOCKET_ERROR)
	{
		cout << "创建套接字失败" << endl;
		return 0;
	}
	if (bind(server_socket, (sockaddr*)&addr_server, sizeof(addr_server)) == SOCKET_ERROR)
	{
		cout << "绑定失败" << endl;
		return 0;
	}
	if (listen(server_socket, MAX_LISTEN) == SOCKET_ERROR)
	{
		cout << "监听失败" << endl;
		return 0;
	}
	return server_socket;
}

int GetHostInfo(char str[2048], char *host,int &port)
{
	int p = 8;
	char url[2048] = { 0 };
	int m = 0;

	for (int i = 0; i < strlen(str); i++, p++)
	{
		if (str[p] == ':')
		{
			host[i] = '\0';
			break;
		}
		host[i] = str[p];
	}
	p++;	// 去掉冒号
	char cPort[8] = { 0 };
	for (int i = 0; i < strlen(str); i++, p++)
	{
		if (str[p] == ' ')
		{
			cPort[i] = '\0';
			break;
		}
		cPort[i] = str[p];
	}
	port = atoi(cPort);
}

// 链接到http服务器
SOCKET ConnectToHttp(char *http_host_ip, int http_port)
{
	SOCKADDR_IN http_addr;
	http_addr.sin_family = AF_INET;
	http_addr.sin_port = htons(http_port);
	http_addr.sin_addr.S_un.S_addr = inet_addr(http_host_ip);
	SOCKET http_socket = socket(AF_INET, SOCK_STREAM, 0);
	if (http_socket == SOCKET_ERROR)
	{
		cout << "创建套接字失败" << endl;
		return 0;
	}
	if (connect(http_socket, (SOCKADDR*)&http_addr, sizeof(http_addr)) == SOCKET_ERROR)
	{
		cout << "连接失败" << endl;
		return 0;
	}
	return http_socket;
}

// 通过域名获取IP
int GetIpByName(char str[2048], char httpIP[128])
{
	struct hostent* host = gethostbyname(str);
	if (!host)
	{
		cout << "获取IP错误" << endl;
		return false;
	}
	sprintf_s(httpIP, 18, "%s", inet_ntoa(*(struct in_addr*)host->h_addr_list[0]));
}

void HttpToRequest(SOCKET http_socket,SOCKET request_socket)
{
	cout << "进入线程 HttpToRequest" << endl;
	char buf[MAX_DATABLOCK] = {0};
	while (1)
	{
		int n = recv(http_socket,buf,MAX_DATABLOCK,0);
		if (n == 0 || n == SOCKET_ERROR)
		{
			break;
		}
		send(request_socket,buf,n,0);
	}
	cout << "结束线程 HttpToRequest" << endl;
}

void ReWriteHeader(char* buf)
{
	char* p = strstr(buf, "http://");
	char* p0 = strchr(p, '\0');
	char* p5 = strstr(buf, "HTTP/"); 
	int len = strlen(buf);
	if (p)
	{
		char* p1 = strchr(p + 7, '/');
		if (p1 && (p5 > p1))
		{
			//转换url到 path
			memcpy(p, p1, (int)(p0 - p1));
			int l = len - (p1 - p);
			buf[l] = '\0';
		}
		else
		{
			char* p2 = strchr(p, ' ');
			memcpy(p + 1, p2, (int)(p0 - p2));
			*p = '/';  //url 没有路径使用根
			int l = len - (p2 - p) + 1;
			buf[l] = '\0';
		}
	}

}

// 从http头获取到host 返回值是布尔类型
void GetHttpHost(char *buf,char *http_host,int &http_port)
{
	//cout << "buf: "<<buf << endl;
	/* 获取主机名称 */
	char* p = strstr(buf,"Host:");
	char* p1 = strchr(p,'\r');		// http头每一行的最后面都是\r\n
	memcpy(http_host,p+6,p1-(p+6)); // 6指的是 "Host: "

	/* 获取端口号 */
	char *p2 = strchr(p + 6, ':');	// 主机和端口的连接符
	char port[10] = {0};
	if (p2 && p2<p1)	// 有指定端口号
	{
		memcpy(port,p2+1,(p1-p2));
		http_port = atoi(port);
	}
	else // 没有指定端口的话 就是80
	{
		http_port = 80;
	}
}

// 接受代理请求链接
void request_handle(SOCKET request_socket)
{
	cout << "进入线程 request_handle" << endl;
	char buf[MAX_DATABLOCK] = {0};
	bool is_https = false;

	char http_host[MAX_DATABLOCK] = {0};
	char http_host_ip[128] = {0};
	int http_port;
	SOCKET http_socket;

	int n = recv(request_socket,buf,MAX_DATABLOCK,0);
	if (n == SOCKET_ERROR || n == 0)
	{
		cout << "断开链接了"<<WSAGetLastError() << endl;
		return ;
	}
	while (1)
	{
		if (strstr(buf, "CONNECT"))	// 是https请求
		{
			is_https = true;
			GetHostInfo(buf, http_host, http_port);
			GetIpByName(http_host, http_host_ip);

			cout << "远端主机" << http_host << ":" << http_port << "\nip: " << http_host_ip << endl;
			http_socket = ConnectToHttp(http_host_ip, http_port);
			if (!http_socket)
			{
				cout << "链接主机失败" << endl;
				return;
			}

			// 响应https 表示可以接受https请求
			send(request_socket, "HTTP/1.1 200 Connection established\r\n\r\n", strlen("HTTP/1.1 200 Connection established\r\n\r\n"), 0);

			thread t(HttpToRequest, http_socket, request_socket);
			t.detach();
			while (1)
			{
				memset(buf, 0, sizeof(buf));
				int n = recv(request_socket, buf, MAX_DATABLOCK, 0);
				if (n == 0 || n == SOCKET_ERROR)
				{
					break;
				}
				// 这里判断是不是有请求头 有请求头就开始处理
				if (strstr(buf, "GET") || strstr(buf, "POST") || strstr(buf, "CONNECT"))	// 是一个新的请求头
				{
					cout << "新的请求头" << endl;
					break;
				}
				
				send(http_socket, buf, n, 0);
			}
		}
		else // http请求
		{
			cout << "http请求" << endl;
			ReWriteHeader(buf); // 重写http头
			GetHttpHost(buf, http_host, http_port);
			GetIpByName(http_host, http_host_ip);
			cout << "主机: " << http_host << " ip:" << http_host_ip << endl;
			cout << buf << endl;

			http_socket = ConnectToHttp(http_host_ip, http_port);
			if (!http_socket)
			{
				cout << "链接主机失败" << endl;
				return;
			}
			send(http_socket, buf, n, 0);
			thread t(HttpToRequest, http_socket, request_socket);
			t.detach();
			while (1)
			{
				int n = recv(request_socket, buf, MAX_DATABLOCK, 0);
				if (n == 0 || n == SOCKET_ERROR)
				{
					break;
				}
				if (strstr(buf, "GET") || strstr(buf, "POST") || strstr(buf, "CONNECT"))	// 是一个新的请求头
				{
					cout << "新的请求头" << endl;
					break;
				}
				send(http_socket, buf, n, 0);
			}
		}
	}
	cout << "退出线程 request_handle" << endl;
}

// 正向代理
int ForwardProxy()
{
	SOCKET server_socket = StartServer();
	if (!server_socket)
	{
		cout << "创建服务失败" << endl;
		return 0;
	}
	SOCKADDR_IN request_addr;
	memset(&request_addr, 0, sizeof(SOCKADDR_IN));
	SOCKET request_socket;
	int len = sizeof(request_addr);
	cout << "准备就绪 等待请求" << endl;
	while (1)	// 主循环 不停的接收请求
	{
		request_socket = accept(server_socket,(sockaddr*)&request_addr,&len);
		if (request_socket == SOCKET_ERROR)
		{
			cout << "链接接受失败" << endl;
			continue;
		}
		thread t(request_handle,request_socket);
		t.detach();
	}
}

// 创建和转发服务器的socket
SOCKET StartClient()
{
	SOCKADDR_IN http_addr;
	http_addr.sin_family = AF_INET;
	http_addr.sin_port = htons(remote_port);
	http_addr.sin_addr.S_un.S_addr = inet_addr(remote_host);
	SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
	if (client_socket == SOCKET_ERROR)
	{
		cout << "创建套接字失败" << endl;
		return SOCKET_ERROR;
	}
	if (connect(client_socket, (SOCKADDR*)&http_addr, sizeof(http_addr)) == SOCKET_ERROR)
	{
		cout << "连接失败" << endl;
		return SOCKET_ERROR;
	}
	return client_socket;
}

// 反向代理
void ReverseProxy()
{
	while (1)	// 断开之后再链接
	{
		SOCKET client_socket = StartClient();
		if (client_socket == SOCKET_ERROR)
		{
			cout << "连接转发服务器失败,检查信息是否正确" << endl;
			return ;
		}
		send(client_socket, "client", strlen("client"), 0);	// 向服务端_async_raise(tid,SystemExit)验证自己是客户端身份
		thread t(request_handle, client_socket);
		t.join();
		cout << "重连" << endl;
	}
}

int main(int argc, char* argv[])
{
	/*获取操作参数*/
	char value[PARAM_SIZE] = { 0 };
	//if (argc == 1)
	//{
	//	ShowHelp();
	//	return 0;
	//}
	if (getopt(argc, argv, "-h", value))					// 指定转发服务器
	{
		if (strcmp(value, ""))
		{
			WORK_MODE = 1;
			memset(remote_host, 0, sizeof(remote_host));	// 清空原来的
			memcpy(remote_host,value,strlen(value));		// 设置远端服务器
			memset(value, 0, sizeof(value));				// 清空value 为了下个参数腾位置
		}
	}else{ ShowHelp(); return 0;}

	if (getopt(argc, argv, "-p", value))					// 指定转发服务器端口
	{
		if (strcmp(value, ""))
		{
			int ret = atoi(value);
			if (ret == 0)
			{
				cout << "指定的端口不合法" << endl;
				ShowHelp();
				return 0;
			}
			else
			{
				remote_port = ret;
				memset(value, 0, sizeof(value));
			}
			memset(value, 0, sizeof(value));
		}
	}else { ShowHelp(); return 0; }

	if (getopt(argc, argv, "-l", value))					// 指定本地端口
	{
		if (strcmp(value, ""))
		{
			int ret = atoi(value);
			if (ret == 0)
			{
				cout << "指定的端口不合法" << endl;
				ShowHelp();
				return 0;
			}
			else
			{
				local_port = ret;
				memset(value, 0, sizeof(value));
			}
			memset(value, 0, sizeof(value));
		}
	}else { ShowHelp(); return 0; }

	/* 初始化套接字 */
	WSADATA wsa_data;
	if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0)
	{
		LOG("[E] 初始化套接字失败");
		return 0;
	}

	if (WORK_MODE)
	{
		cout << "工作模式为：反向代理,转发服务器为"<<remote_host<<":"<<remote_port << endl;
		ReverseProxy();
	}
	else
	{
		cout << "工作模式为：正向代理，监听端口"<<local_port << endl;
		ForwardProxy();
	}
	return 0;
}